﻿namespace Nomo.CoreModule
{
    /// <summary>
    /// 完全展开的枚举器。
    /// </summary>
    public readonly struct UnfoldedEnumerator : System.Collections.IEnumerator
    {
        private readonly System.Collections.IEnumerator                                   _enumerator;
        private readonly System.Collections.Generic.Stack<System.Collections.IEnumerator> _stack;

        /// <summary>
        /// 使用指定的枚举器初始化 <see cref="UnfoldedEnumerator"/> 类的新实例。
        /// </summary>
        /// <param name="enumerator">枚举器。</param>
        /// <exception cref="System.ArgumentNullException"><paramref name="enumerator"/> 为 <see langword="null"/> .</exception>
        public UnfoldedEnumerator(System.Collections.IEnumerator enumerator)
        {
            if (enumerator == null)
            {
                throw new System.ArgumentNullException(nameof(enumerator));
            }
            _enumerator = enumerator;
            _stack      = new System.Collections.Generic.Stack<System.Collections.IEnumerator>(1);
            _stack.Push(enumerator);
        }

        /// <summary>
        /// 特别添加此方法，以便能够使用 <see langword="foreach"/> 关键字枚举此实例。
        /// </summary>
        /// <returns>返回此实例本身。</returns>
        public UnfoldedEnumerator GetEnumerator()
        {
            return this;
        }

        /// <summary>
        /// 将枚举器的位置向后推进一步。
        /// </summary>
        /// <returns>是否成功地向后推进了一步？</returns>
        public bool MoveNext()
        {
            while (_stack.Count != 0)
            {
                var stackTopEnumerator = _stack.Peek();
                if (stackTopEnumerator.MoveNext())
                {
                    if (stackTopEnumerator.Current is System.Collections.IEnumerator enumerator)
                    {
                        _stack.Push(enumerator);
                    }
                    else
                    {
                        return true;
                    }
                }
                else
                {
                    _stack.Pop();
                }
            }
            return false;
        }

        /// <summary>
        /// 获取位于枚举器当前位置的元素。
        /// </summary>
        /// <exception cref="System.InvalidOperationException">枚举器的当前位置已经到达了末尾。</exception>
        /// <remarks>
        /// 如果枚举器当前的元素仍然是一个枚举器，会递归地获取子枚举器的当前位置的元素，直到当前的元素不是枚举器。换句话说，永远不会返回一个枚举器类型的实例。
        /// </remarks>
        public object Current
        {
            get
            {
                if (_stack.Count == 0)
                {
                    throw new System.InvalidOperationException();
                }
                return _stack.Peek().Current;
            }
        }

        /// <summary>
        /// 将枚举器的位置设置到第一个元素之前。
        /// </summary>
        /// <remarks>
        /// 此方法会调用在创建此实例时向 <see cref="UnfoldedEnumerator(System.Collections.IEnumerator)"/> 传入的枚举器的 <see cref="System.Collections.IEnumerator.Reset"/> 方法，此过程可能会抛出异常。
        /// </remarks>
        public void Reset()
        {
            _enumerator.Reset();
            if (_stack.Count != 0)
            {
                _stack.Clear();
            }
            _stack.Push(_enumerator);
        }
    }
}
